package dungeon;

import be.kuleuven.cs.som.annotate.Basic;
import be.kuleuven.cs.som.annotate.Immutable;
import be.kuleuven.cs.som.annotate.Raw;
import be.kuleuven.cs.som.annotate.Value;

/**
 * A class of position values involving an x, y and z coordinate.
 * 
 * @version 2.0
 * @author Inge Gielis <inge.gielis@student.kuleuven.be>
 * @author Annelies Ketelslegers <annelies.ketelslegers@student.kuleuven.be>
 *
 */
@Value
public class Position{
	
	/**
	 * Initialize this new position, with the given values for the x, y and z coordinates.
	 * 
	 * @param	x
	 * 			The x coordinate for this new position.
	 * @param	y
	 * 			The y coordinate for this new position.
	 * @param	z
	 * 			The z coordinate for this new position.
	 * @post	The new x coordinate  of the position is equal to the given x coordinate.
	 * 			| new.getX() == x
	 * @post	The new y coordinate  of the position is equal to the given y coordinate.
	 * 			| new.getY() == y
	 * @post	The new z coordinate  of the position is equal to the given z coordinate.
	 * 			| new.getZ() == z
	 * @throws	IllegalArgumentException()
	 * 			One, some or all of the given coordinates are negative.
	 * 			| ((x<0) || (y<0)) || (z<0)
	 * @throws	IllegalArgumentException()
	 * 			The value of one, some or all of the given coordinates exceeds the maximum value for a coordinate.
	 * 			| ((x>= Long.MAX_VALUE) || (y>= Long.MAX_VALUE)) || (z>= Long.MAX_VALUE)
	 * @note	Assume that there are no negative values for coordinates, 
	 * 			because then it would not be possible to define a position
	 * 			for the leftmost square, at the front and bottom of the coordinate system. 
	 */
	@Raw
	public Position(long x, long y, long z){
		if(((x<0) || (y<0)) || (z<0))
			throw new IllegalArgumentException("All values must be positive");
		if(((x>= Long.MAX_VALUE) || (y>= Long.MAX_VALUE)) || (z>= Long.MAX_VALUE))
			throw new IllegalArgumentException("All values must be smaller than long.MAX_VALUE");
		this.x = x;
		this.y = y;
		this.z = z;
	}
	
	/**
	 * Check whether this position is equal to the given object.
	 * 
	 * @return	True if and only if the given object is effective, 
	 * 			and if this position and the given object belong to the same class, 
	 * 			and if this position and the other object interpreted as a position have equal x-, y- and z-coordinates.
	 * 			| result ==
	 * 			|	(( other != null)
	 * 			|	&& (this.getClass() == other.getClass())
	 * 			|	&& (((Position) other).getX() == this.getX()) 
	 *			|	&& (((Position) other).getY() == this.getY())) 
	 *			|	&& (((Position) other).getZ() == this.getZ()))
	 * 
	 */
	@Override
	public boolean equals(Object other){
		if(other ==  null)
			return false;
		if(this.getClass() != other.getClass())
			return false;
		Position otherPosition = (Position) other;
		return((otherPosition.getX() == this.getX()) 
			&& (otherPosition.getY() == this.getY()) 
			&& (otherPosition.getZ() == this.getZ()));
	}
	
	/**
	 * Return the hash code for this position. 
	 */
	@Override
	public int hashCode(){
		return (int) (this.getX()*1000000 + this.getY()*1000 + this.getZ());
	}
	
	/**
	 * Return the x-coordinate of this position.
	 */
	@Basic @Immutable
	public long getX(){
		return this.x;
	}
	
	/**
	 * Return the y-coordinate of this position.
	 */
	@Basic @Immutable
	public long getY(){
		return this.y;
	}
	
	/**
	 * Return the z-coordinate of this position.
	 */
	@Basic @Immutable
	public long getZ(){
		return this.z;
	}
	
	/**
	 * Variable referencing the x-coordinate of this position.
	 */
	private long x;
	
	/**
	 * Variable referencing the y-coordinate of this position.
	 */
	private long y;
	
	/**
	 * Variable referencing the z-coordinate of this position.
	 */
	private long z;
	
	/**
	 * Return a textual representation of this position.
	 * 
	 * @return	A string consisting of the textual representation of the coordniates of this position,
	 * 			of the form (x-coordinate; y-coordinate; z-coordinate).
	 * 			| result.equals("Position: " + "("+getX()+"; "+getY()+"; "+getZ()+")")	
	 */
	@Override
	public String toString(){
		String returnString = "";
		returnString += "Position: " + "("+getX()+"; "+getY()+"; "+getZ()+")";
		return returnString;
	}
	
	/**
	 * Subtract the given position from this position.
	 * 
	 * @param position
	 * 			The position to subtract.
	 * @return	A new position with new coordinates.
	 * 			|result == 
	 * 			|	new Position(this.getX()-position.getX(), this.getY()-position.getY(),this.getZ()-position.getZ())	
	 */			
	public Position subtract(Position position){
		return new Position(this.getX()-position.getX(), this.getY()-position.getY(),this.getZ()-position.getZ());
	}
	
	/**
	 * Add the given position to this position
	 * 
	 * @param	position
	 * 			The position to add.
	 * @return	A new position with new coordinates.
	 * 			|result == 
	 * 			|	new Position(this.getX()+position.getX(), this.getY()+position.getY(),this.getZ()+position.getZ())
	 * 		
	 */
	public Position add(Position position){
		return new Position(this.getX()+position.getX(), this.getY()+position.getY(),this.getZ()+position.getZ());
	}

}
